﻿/*!
 * Copyright (c), 上海沃恩信息科技有限公司.
 * All rights reserved.
 * Licensed under BSD (https://www.pittypat.work/bsd.html).
 */

using System;
using System.Data.Common;

namespace Pittypat
{
    /// <summary>
    /// 从 DbDataReader 中读取 <see cref="IPrimitive"/> 值。
    /// </summary>
    public static class DataReaderExtensions
    {
        #region GetValue Methods

        /// <summary>
        /// 从 DbDataReader 中读取指定列名称的值，并将值转换为目标类型。
        /// </summary>
        /// <typeparam name="T">要读取的值转换到的类型。</typeparam>
        /// <param name="reader">要从其中读取值的 DbDataReader 对象，必须事先已经通过 Read 方法定位到某一数据行。</param>
        /// <param name="name">列的名称。</param>
        /// <param name="defaultValue">如果值无法转换为目标类型，或列不存在时返回的默认值。</param>
        /// <returns>从 reader 中读取的值，如果不能转换为目标类型，或者列不存在，则返回 defaultValue。</returns>
        /// <exception cref="ArgumentNullException">reader 为 null，或者 name 为 null 或空字符串。</exception>
        public static T? GetValue<T>(this DbDataReader reader, string name, T? defaultValue = null) 
            where T : struct, IPrimitive
        {
            if (reader == null)
            {
                throw new ArgumentNullException("reader");
            }

            if (string.IsNullOrEmpty(name))
            {
                throw new ArgumentNullException("name");
            }

            // 列在 DataReader 中的索引位置
            int ordinal = -1;

            try
            {
                ordinal = reader.GetOrdinal(name);
            }
            catch (IndexOutOfRangeException)
            {
                // 指定名称的列不存在
                ordinal = -1;
            }

            return ordinal < 0 ? defaultValue : GetValue<T>(reader, ordinal, defaultValue);
        }

        /// <summary>
        /// 从 DbDataReader 中读取指定列位置上的值，并将值转换为目标类型。
        /// </summary>
        /// <typeparam name="T">要读取的值转换到的类型。</typeparam>
        /// <param name="reader">要从其中读取值的 DbDataReader 对象，必须事先已经通过 Read 方法定位到某一数据行。</param>
        /// <param name="ordinal">列在 DbDataReader 中的索引位置，从 0 开始。</param>
        /// <param name="defaultValue">如果值无法转换为目标类型时返回的默认值。</param>
        /// <returns>从 reader 中读取的值，如果不能转换为目标类型，则返回 defaultValue。</returns>
        /// <exception cref="ArgumentNullException">reader 为 null。</exception>
        /// <exception cref="ArgumentOutOfRangeException">ordinal 小于 0，或者大于等于字段数目。</exception>
        public static T? GetValue<T>(this DbDataReader reader, int ordinal, T? defaultValue = null) 
            where T : struct, IPrimitive
        {
            if (reader == null)
            {
                throw new ArgumentNullException("reader");
            }

            // 检查目标列是否是数据库空值
            object value = reader.IsDBNull(ordinal) ? null : reader.GetValue(ordinal);
            if (value == null)
            {
                // 数据库空值
                return null;
            }

            // 转换为目标类型
            object result = value.To<T>();
            return (T?)(result ?? defaultValue);
        }

        #endregion
    }
}
